﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using MentalAlchemy.Atomics;
using MentalAlchemy.Molecules.NevaAlgorithm.nevamod;

namespace MentalAlchemy.Molecules.NevaAlgorithm.Compounds
{
	public partial class OperatorsAnalyzerControl : UserControl
	{
		protected OperatorsAnalyzer opsAnalyzer;

		#region - Properties. -
		public OperatorsAnalyzer OpsAnalyzer
		{
			get { return opsAnalyzer; }
			set
			{
				opsAnalyzer = value;

				// show operations.
				if (opsAnalyzer.Changes != null)
				{
					var lines = Neva2Elements.ConvertToStrings(opsAnalyzer.Changes);
					DataBox.Lines = lines.ToArray();

					var maxGen = OperatorsAnalyzer.GetMaxGeneration(opsAnalyzer.Changes);
					MaxGenNumeric.Value = maxGen;
				}
			}
		}

		/// <summary>
		/// Status for check box indicating whether minimization or maximizaiton problem data is analyzed.
		/// </summary>
		public bool MinimizationProblem
		{
			get { return MinimizationCheck.Checked; }
			set { MinimizationCheck.Checked = value; }
		}
		#endregion

		public OperatorsAnalyzerControl()
		{
			InitializeComponent();
		}

		private void RunBtn_Click(object sender, EventArgs e)
		{
			if (OpsAnalyzer == null || OpsAnalyzer.Changes.Count == 0) return;

			// 0. perform trimming by max generation.
			var chs = OperatorsAnalyzer.TrimByGeneration(OpsAnalyzer.Changes, (int)MaxGenNumeric.Value);
			var chsStrL = OperatorsAnalyzer.ConvertToStrings(chs);
			var chsStr = Strings.Concat(chsStrL.ToArray());

			// 1. take source data and count basic stats.
			var counts = VectorMath.CountEntries(chs.ToArray());

			// 1.1 count fitness delta stats.
			var stats = OperatorsAnalyzer.CalculateFitnessDeltaStats(chs);

			// 1.2 show fitness delta stats.
			var lines = new List<string>();
			foreach (var stat in stats)
			{
				lines.Add(stat.Key + "\t" + stat.Value.GetStatsString());
			}
			StatsBox.Lines = lines.ToArray();

			// 2. count subseqs of the requested length.
			lines = new List<string>();
			for (int len = 2; len < 11; len++)
			{
				var subIdx = Strings.FindRepSubstrings(chsStr, len);

				// 2.0.1. Compute stats.
				var subStats = new Dictionary<string, Stats>();
				var fitDeltas = OperatorsAnalyzer.GetFitnessDeltaValues(chs);
				foreach (var pair in subIdx)
				{
					var sample = VectorMath.SampleByIndices(fitDeltas, pair.Value);
					subStats.Add(pair.Key, VectorMath.CalculateStats(sample));
				}

				// 2.1. show substrings basic stats.
				lines.Add(len.ToString());
				foreach (var subId in subIdx)
				{
					lines.Add(subId.Key + "\t" + subId.Value.Length + "\t" + subStats[subId.Key].GetStatsString());
				}
			}
			SubseqStatsBox.Lines = lines.ToArray();

			//
			// compute operators transitions only for successfull operations.
			var trans = OperatorsAnalyzer.ComputeSuccessfulTransitions(chs, MinimizationProblem);

			// convert transition matrix into list of string arrays to write into [TransDataTable]
			var ops = Operators.GetMutOperators();
			var transM = OperatorsAnalyzer.TransitionsToMatrix(trans, ops);
			var transRows = MatrixMath.ConvertToRowsList(transM);
			var transStr = MatrixMath.ConvertToStringsList(transRows);

			TransDataTable.DataRows = transStr;
			TransDataTable.Header = ops;
		}

		/// <summary>
		/// Update changes.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void DataBox_TextChanged(object sender, EventArgs e)
		{
			var lines = DataBox.Lines;

			if (OpsAnalyzer == null) {OpsAnalyzer = new OperatorsAnalyzer();}
			var chs = new List<NevaIndChanges>();
			foreach (var line in lines)
			{
				var ch = NevaIndChanges.FromString(line);
				chs.Add(ch);
			}
			OpsAnalyzer.Changes = chs;
		}

		private void SaveLogBtn_Click(object sender, EventArgs e)
		{
			var filename = "log.log";

			// if no calculations were made, then run them.
			if (TransDataTable.DataRows == null || TransDataTable.DataRows.Count == 0)
			{
				RunBtn_Click(this, null);
			}

			using (var writer = new StreamWriter(filename))
			{
				// 1. save initial data.
				writer.WriteLine("> Source changes:");
				FileIO.WriteAllLines(writer, DataBox.Lines);

				// 2. save single operators stats.
				writer.WriteLine("\n> Operators stats:");
				FileIO.WriteAllLines(writer, StatsBox.Lines);

				// 2.1. save table for operators utility over time
				var timeUtil = OperatorsAnalyzer.CalculateOpsTemporalSuccess(OpsAnalyzer.Changes);
				var timeUtilStr = MatrixMath.ConvertToRowsStringsList(timeUtil, '\t');
				writer.WriteLine("\n> Operators' temporal utility:");
				FileIO.WriteAllLines(writer, timeUtilStr);

				// 2.2. save data on operators usage by generations
				var genData = OperatorsAnalyzer.GetGenerationsData(OpsAnalyzer.Changes);
				writer.WriteLine("\n> Operators usage by generations:");
				foreach (var pair in genData)
				{
					var getDataStr = VectorMath.ConvertToString(pair.Value, '\t');
					writer.WriteLine(pair.Key + "\t" + getDataStr);
				}

                // 3. save successfull transitions matrix.
				List<string> transData;
				if (SciDavisFormatCheck.Checked)
				{
					var transM = MatrixMath.CreateFromRowsList(TransDataTable.DataRows);
					transData = MatrixMath.ConvertToSurfaceDescr(transM);
				}
				else
				{
					transData = MatrixMath.ConvertToRowsStringsList(TransDataTable.DataRows, '\t');
				}
				writer.WriteLine("\n> Successfull transitions matrix:");
				FileIO.WriteAllLines(writer, transData);

				// 4. save sequences stats.
				writer.WriteLine("\n> Sequences stats:");
				FileIO.WriteAllLines(writer, SubseqStatsBox.Lines);
			}
		}
	}

	/// <summary>
	/// [compounds]
	/// 
	/// A form containing only [OperatorsAnalyzerControl] with a [fill] docking style.
	/// </summary>
	public class OperatorsAnalyzerForm : Form
	{
		public OperatorsAnalyzerControl OperatorsAnalyzerControl { get { return (OperatorsAnalyzerControl)Controls[0]; } }

		#region - Public static methods. -
		/// <summary>
		/// [compound]
		/// 
		/// Returns new form containing only [OperatorsAnalyzerControl] with a [fill] docking style.
		/// </summary>
		/// <returns></returns>
		public static OperatorsAnalyzerForm Create()
		{
			var res = new OperatorsAnalyzerForm();
			res.Controls.Add(new OperatorsAnalyzerControl());
			res.Controls[0].Dock = DockStyle.Fill;
			return res;
		}
		#endregion
	}
}
